/**
 *
 */
package com.legrand.security.jwt;

import com.legrand.core.exception.UnAuthorizedException;
import com.legrand.security.token.JwtAuthenticationToken;
import com.legrand.security.token.RawAccessJwtToken;
import com.legrand.security.token.UserContext;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;

import javax.annotation.PostConstruct;
import java.io.ByteArrayInputStream;
import java.security.PublicKey;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
*@author xiah
*2018-07-12 01:34:01
*/
@Component
public class JwtAuthenticationProvider implements AuthenticationProvider {

    private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationProvider.class);

    public final static String KEY = "-----BEGIN CERTIFICATE-----\n"
            + "MIICNTCCAZ6gAwIBAgIES343gjANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJV\n"
            + "UzELMAkGA1UECAwCQ0ExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxDTALBgNVBAoM\n"
            + "BFdTTzIxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xMDAyMTkwNzAyMjZaFw0zNTAy\n"
            + "MTMwNzAyMjZaMFUxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEWMBQGA1UEBwwN\n"
            + "TW91bnRhaW4gVmlldzENMAsGA1UECgwEV1NPMjESMBAGA1UEAwwJbG9jYWxob3N0\n"
            + "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCUp/oV1vWc8/TkQSiAvTousMzO\n"
            + "M4asB2iltr2QKozni5aVFu818MpOLZIr8LMnTzWllJvvaA5RAAdpbECb+48FjbBe\n"
            + "0hseUdN5HpwvnH/DW8ZccGvk53I6Orq7hLCv1ZHtuOCokghz/ATrhyPq+QktMfXn\n"
            + "RS4HrKGJTzxaCcU7OQIDAQABoxIwEDAOBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcN\n"
            + "AQEFBQADgYEAW5wPR7cr1LAdq+IrR44iQlRG5ITCZXY9hI0PygLP2rHANh+PYfTm\n"
            + "xbuOnykNGyhM6FjFLbW2uZHQTY1jMrPprjOrmyK5sjJRO4d1DeGHT/YnIjs9JogR\n"
            + "Kv4XHECwLtIVdAbIdWHEtVZJyMSktcyysFcvuhPQK8Qc/E/Wq8uHSCo=\n" + "-----END CERTIFICATE-----";

    private PublicKey publicKey;

    @PostConstruct
    public void init() {
        CertificateFactory ft;
        X509Certificate certificate = null;
        try {
            ft = CertificateFactory.getInstance("X.509");
            certificate = (X509Certificate) ft.generateCertificate(new ByteArrayInputStream(KEY.getBytes()));
        } catch (CertificateException e) {
            ReflectionUtils.rethrowRuntimeException(e);
        }
        this.publicKey = certificate.getPublicKey();
    }

    @SuppressWarnings("unchecked")
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        RawAccessJwtToken rawAccessToken = (RawAccessJwtToken) authentication.getCredentials();
        logger.debug("Current token is: {}", rawAccessToken);

        Jws<Claims> jwsClaims = rawAccessToken.parseClaims(publicKey);
        final Integer userId = jwsClaims.getBody().get("userId", Integer.class);
        final String username = jwsClaims.getBody().get("userName", String.class);
        final String telephone = jwsClaims.getBody().get("telephone", String.class);
        final List<String> roles = new ArrayList<String>();//jwsClaims.getBody().get("roles", List.class);
        roles.add("ROLE_USER");
        if (null == roles || roles.isEmpty() || roles.size() <= 0) {
            logger.error("This user hasn't role... ");
            throw new UnAuthorizedException();
        }
        List<GrantedAuthority> authorities = roles.stream().map(authority -> new SimpleGrantedAuthority
                (authority))
                .collect(Collectors.toList());
        UserContext context = new UserContext(userId, username, telephone, roles);
        return new JwtAuthenticationToken(context, authorities);
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return (JwtAuthenticationToken.class.isAssignableFrom(authentication));
    }

}
